//// encoding=UTF-8
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include <wait.h>
#include <unistd.h>
#include <fcntl.h>
#define maxx 1025

char normal[20]="\e[0;39;49m", black[30]="\e[40m", red[30]="\e[1;41m", green[30]="\e[42m", yellow[30]="\e[43m", blue[30]="\e[44m", magenta[30]="\e[45m", cyan[30]="\e[46m", white[30]="\e[1;38;2;0;0;0;47m";

//// 返回-1表示不是内建命令
int innerCMD(char command[][maxx], int index) {
    int rtn=0;
    //// ch是shell内建命令
    if (strcmp(command[0], "cd") == 0) {
        if(command[1] == NULL) {
            chdir(getenv("HOME"));
            return 0;
        }
            
        rtn=chdir(command[1]);
        switch (rtn) {
            case 0: return 0;
            default: return 2;
        }
    }
    return -1;
}

//// 里面的所有命令都在子进程里。
int exeCMD(char command[][maxx], int index) {
    char *argument[128]={NULL};
    int i;
    //// ls加入颜色，注意最后一个参数一定得为NULL
    if (strcmp(command[0], "ls") == 0) {
        strcpy(command[index],"--color=auto");
        index++;
    }
    for (i = 0; i < index; i++) {
        argument[i]=&command[i];
    }
    execvp(argument[0], argument);
    fprintf(stderr, "%sFailed%s\n",red, normal);
    exit(-1);
}

int PS1() {
    char *u, *h, *w;
    u=getenv("USER"); h=getenv("NAME");
    w=getcwd(NULL,NULL);
    fprintf(stdout, "\e[1;32m%s@%s:\e[1;34m%s\e[0m$ ", u, h, w);
    return 0;
}

//// 使用类似网络层PPP协议进行字符替换
void encode(char (*str)[]) {
    char temp;
    int flag1=0, len=strlen(*str);
    int i;
    for (i = 0; i < len; i++) {
        if ((*str)[i] == '"') {
            flag1^=1;
            continue;
        }
        //// 在双引号内
        if (flag1 != 0) {
            switch((*str)[i]) {
                case ' ': (*str)[i]=3;  break;
                case '|': (*str)[i]=4;  break;
                case '>': (*str)[i]=5;  break;
                case '<': (*str)[i]=6;  break;
                default: temp='a';
            }
        }
    }
}

void decode(char (*str)[]) {
    char tmp_buf[maxx];
    int len=strlen(*str),tmp_len=0;
    int i;
    for (i = 0; i < len; i++) {
        switch((*str)[i]) {
            case '"': break;
            case 3: tmp_buf[tmp_len++]=' '; break;
            case 4: tmp_buf[tmp_len++]='|'; break;
            case 5: tmp_buf[tmp_len++]='>'; break;
            case 6: tmp_buf[tmp_len++]='<'; break;
            default: tmp_buf[tmp_len++]=(*str)[i];
        }
    }
    tmp_buf[tmp_len++]='\0';
    strcpy((*str), tmp_buf);
}

int main(int argc, char *argcv[]) {

    char buf[maxx], ch, buf2[maxx]={0};
    char command[128][40][maxx];        //// 存储参数，[0]为命令本身
    char *cmd_ptr=NULL;
    char *tmp_ptr=NULL, fileToWrite[128][maxx], fileToRead[128][maxx];
    int index,status, cntCMD=0;
    char school_number_21231017=0; 
    int fd_write, org_stdout, fd_read, org_stdin;
    while(1) {
        PS1();
    
        if (school_number_21231017 == 0) { cntCMD = 0; }
        //// 读入
        fgets(buf, maxx, stdin);
        buf[strlen(buf)-1]='\0';
        
        //// 0.只输入回车，需要特殊判断
        if (strlen(buf) == 0) { continue; }

        //// 1.引号的相关处理
        encode(&buf);
        //// 多行命令
        if (buf[strlen(buf)-1]=='\\') { printf("侯甸"); }
        
        //// 有管道命令，需要先分组命令。分组后，buf分在了各个command[cntCMD][0]里
        int i = cntCMD, i2 = cntCMD;

        //// NOTE:cntCMD不需要置0
        //// 2.分割管道命令
        cmd_ptr=strtok_r(buf, "|", &tmp_ptr);
        while(cmd_ptr != NULL) {
            //// 3.提取重定向的符号
            strcpy(command[cntCMD++][0], cmd_ptr);
            cmd_ptr=strtok_r(NULL, "|", &tmp_ptr);

        }

        //// 输出重定向
        i2=i;
        while(i2 < cntCMD) {
            strcpy(buf2, command[i2][0]);
            tmp_ptr=NULL;
            cmd_ptr=strtok_r(buf2, ">", &tmp_ptr);
            strcpy(command[i2][0], cmd_ptr);
            cmd_ptr=strtok_r(NULL, " >", &tmp_ptr);
            if (cmd_ptr != NULL) 
                strcpy(fileToWrite[i2], cmd_ptr);
            else
                fileToWrite[i2][0]='\0';
            i2++;
        }


        //// 输入重定向
        i2=i;
        tmp_ptr=NULL;
        while(i2 < cntCMD) {
            strcpy(buf2, command[i2][0]);
            cmd_ptr=strtok_r(buf2, "<", &tmp_ptr);
            strcpy(command[i2][0], cmd_ptr);
            cmd_ptr=strtok_r(NULL, " <", &tmp_ptr);
            if (cmd_ptr != NULL) 
                strcpy(fileToRead[i2], cmd_ptr);
            else
                fileToRead[i2][0]='\0';
            i2++;
        }

    
        //// 只支持单条指令进行重定向
        
        
        school_number_21231017=0;

	    int previous_out_fd, fd[2];
        
        //// 对每组命令进行处理
        for (; i < cntCMD; i++) {
            org_stdout = dup(STDOUT_FILENO);
            org_stdin = dup(STDIN_FILENO);
            fd_write=org_stdout;
            fd_read=org_stdin;
            if (strcmp(fileToWrite[i],"") != 0) {
                fd_write=open(fileToWrite[i], O_WRONLY|O_CREAT|O_TRUNC, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
                if(fd_write < 0)
                {
                    fprintf(stderr, "打开文件错误\n");
                    continue;
                }
                dup2(fd_write, STDOUT_FILENO);
            }
            if (strcmp(fileToRead[i],"") != 0) {
                fd_read=open(fileToRead[i], O_RDONLY);
                if(fd_read < 0)
                {
                    fprintf(stderr, "打开文件错误\n");
                    continue;
                }
                dup2(fd_read, STDIN_FILENO);
            }
            strcpy(buf2, command[i][0]);
            if( i == 0 ) 
                previous_out_fd=dup(STDIN_FILENO);

            //// 4.分割空格
            cmd_ptr=strtok_r(buf2, " ", &tmp_ptr);
            index=0;
            while(cmd_ptr != NULL) {
                strcpy(command[i][index++], cmd_ptr);
                decode(command[i][index-1]);
                cmd_ptr=strtok_r(NULL, " ", &tmp_ptr);
            }
            //// 命令处理完毕


            //// shell终止条件：输入exit
            if (strcmp(command[i][0],"exit") == 0) { return 0; }

		    if (pipe(fd) == -1)
		    {
		    	perror("pipe create error");
		    	return errno;
		    }

            //// 内建命令的执行。内建命令不考虑管道
            switch (innerCMD(command[i],index)) {
                case -1: break;
                case 2: perror(command[i][1]);
                default: continue;
            }
            //// 外部命令的执行
            int son_pid;
            pid_t id=fork();
            if (id == 0) {
                //// 注意：以下代码在子进程中运行
                son_pid=getpid();
                dup2(previous_out_fd, STDIN_FILENO);    //// son的输入成了previous_out_fd
                close(previous_out_fd);
			    close(fd[0]);                           // 关闭son的pipe读端，因为没有直接用
                
                //// 非最后一个，都需要重定向
                if (i <= cntCMD-2) {
                    dup2(fd[1], STDOUT_FILENO);         //// son的输出给了管道的输出
                }
                close(fd[1]);
                
                exeCMD(command[i], index);
                break;
            }
            else {
                // wait(&status);
			    previous_out_fd = fd[0];
			    close(fd[1]); 
		        waitpid(son_pid, &status, 0);
		        int ret2 = WEXITSTATUS(status);
		        if (ret2 != 0) {
		        	fprintf(stderr,"%d\n",ret2);
                    continue;
                }
            }
            close(fd_write);
            dup2(org_stdout, STDOUT_FILENO);
            close(fd_read);
            dup2(org_stdin, STDIN_FILENO);
        }
    }
    return 0;
}
